package com.dc.juc.example._3_atomic;

import com.dc.juc.annoations.ThreadSafe;
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.LongAdder;

/**
 * Description:
 *  LongAdder类与AtomicLong类的区别在于高并发时前者将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作，取值时进行求和；
 *  而在并发较低时仅对base变量进行CAS操作，与AtomicLong类原理相同。
 *
 * 在低竞争的并发环境下 AtomicInteger 的性能是要比 LongAdder 的性能好，而高竞争环境下 LongAdder 的性能比 AtomicInteger 好。
 *  [1] 因为 AtomicInteger 在高并发环境下会有多个线程去竞争一个原子变量，
 *  而始终只有一个线程能竞争成功，而其他线程会一直通过 CAS 自旋尝试获取此原子变量，因此会有一定的性能消耗；
 *  [2] 而 LongAdder 会将这个原子变量分离成一个 Cell 数组，每个线程通过 Hash 获取到自己数组，这样就减少了乐观锁的重试次数，从而在高竞争下获得优势；
 *  而在低竞争下表现的又不是很好，可能是因为自己本身机制的执行时间大于了锁竞争的自旋时间，因此在低竞争下表现性能不如 AtomicInteger。
 *
 * @author: gdc
 * @date: 2021/7/28
 * @version 1.0
 */
@ThreadSafe
@Slf4j
public class _3_LongAdder {

    /**
     * 请求总数
     */
    public static int CLIENT_TOTAL = 5000;
    /**
     * 同时并发执行的线程数
     */
    public static int THREAD_TOTAL = 200;

    /**
     * 累加变量
     *  此处将 COUNT 计数，由int修改为 AtomicLong,是线程安全
     */
    public static LongAdder COUNT = new LongAdder();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        Semaphore semaphore = new Semaphore(THREAD_TOTAL);
        CountDownLatch countDownLatch = new CountDownLatch(CLIENT_TOTAL);


        for (int i = 0; i < CLIENT_TOTAL; i++) {
            threadPool.execute(() -> {
                try {
                    semaphore.acquire();

                    add();

                    semaphore.release();
                } catch (InterruptedException e) {
                    log.error("异常信息为：{}", Throwables.getStackTraceAsString(e));
                } finally {
                    countDownLatch.countDown();
                }
            });
        }

        countDownLatch.await();
        threadPool.shutdown();
        log.info("count:{}", COUNT);
    }

    /**
     * COUNT 计数 +1
     */
    private static void add() {
        COUNT.increment();
    }
}
